/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.bef.engine.repository.adaptor;

import com.inspur.edp.bef.api.BefRtBeanUtil;
import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.bizentity.gspbusinessobject.ParallelTable;
import com.inspur.edp.bef.engine.core.be.EngineBEManager;
import com.inspur.edp.bef.engine.entity.BeModelResInfo;
import com.inspur.edp.bef.engine.entity.EngineChildData;
import com.inspur.edp.bef.engine.entity.EngineRootData;
import com.inspur.edp.bef.engine.repository.exception.BefRespositoryException;
import com.inspur.edp.bef.engine.repository.exception.ErrorCodes;
import com.inspur.edp.bef.engine.repository.typetransprocesser.EnumEngineIntProcesser;
import com.inspur.edp.bef.engine.repository.typetransprocesser.EnumEngineVarcharProcesser;
import com.inspur.edp.bef.engine.repository.typetransprocesser.VarcharIntTransProcesser;
import com.inspur.edp.bef.engine.repository.typetransprocesser.VarcharLongTransProcesser;
import com.inspur.edp.bef.engine.repository.util.ExceptionUtil;
import com.inspur.edp.bef.engine.repository.util.ReadUtils;
import com.inspur.edp.bef.engine.util.DataTypeConvertor;
import com.inspur.edp.cef.api.repository.GspDbDataType;
import com.inspur.edp.cef.api.repository.GspDbType;
import com.inspur.edp.cef.api.repository.IRootRepository;
import com.inspur.edp.cef.api.repository.ITypeTransProcesser;
import com.inspur.edp.cef.api.repository.readerWriter.ICefReader;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.*;
import com.inspur.edp.cef.designtime.api.entity.GspCommonField;
import com.inspur.edp.cef.entity.condition.FilterCondition;
import com.inspur.edp.cef.entity.condition.SortCondition;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.repository.adaptor.EntityRelationalAdaptor;
import com.inspur.edp.cef.repository.adaptor.KeyWordsManager;
import com.inspur.edp.cef.repository.adaptoritem.sqlsnippetprocessor.SqlGeneratorFactory;
import com.inspur.edp.cef.repository.assembler.LogicDeleteInfo;
import com.inspur.edp.cef.repository.dac.EntityDac;
import com.inspur.edp.cef.repository.dbcolumninfo.DbColumnInfo;
import com.inspur.edp.cef.repository.sqlgenerator.BaseSqlGenerator;
import com.inspur.edp.cef.repository.typetransprocesser.*;
import com.inspur.edp.cef.repository.utils.TenantUtil;
import com.inspur.edp.cef.spi.entity.DeleteCheckState;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefEntityResInfoImpl;
import com.inspur.edp.cef.spi.repository.ParallelTableInfo;
import com.inspur.edp.commonmodel.engine.api.common.CMEngineUtil;
import com.inspur.edp.commonmodel.engine.api.data.AssociationInfo;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonModel;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import io.iec.edp.caf.databaseobject.api.entity.DatabaseObjectColumn;
import io.iec.edp.caf.databaseobject.api.entity.DatabaseObjectTableCore;
import lombok.var;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.inspur.edp.bef.engine.repository.util.ReadUtils.encryptIfEnabled;

public class BefBaseAdaptor extends EntityRelationalAdaptor {

    private GspBizEntityObject bizEntityObject;
    private HashMap<String, Integer> propIndexMappingDict;
    private String tableAlias;
    private String deleteSql;
    private String modifySql;
    private String retrieveBatchSql;
    private String retrieveSql;
    private String insertSql;
    private boolean isChildAdaptor;
    private boolean IsEnableEncryption;
    private BaseSqlGenerator sqlGenerator;
    //    private DatabaseObjectTable dbo;
    private DatabaseObjectTableCore dbo;
    private EntityDac entityDac;
    private IGspCommonElement parentIdEle;
    private final Map<String, DatabaseObjectTableCore> parallelTableDboInfos = new HashMap<>();
    private final Map<String, ParallelTable> parallelTableMap = new HashMap<>();

    protected BaseSqlGenerator getSqlGenerator(GspDbType gspDbType) {
        return (BaseSqlGenerator) SqlGeneratorFactory.getSqlProcessor(gspDbType);
    }

    public BefBaseAdaptor(GspBizEntityObject bizEntityObject, EntityDac entityDac, GspDbType gspDbType) {
        super(false);
        this.bizEntityObject = bizEntityObject;
        dbo = (DatabaseObjectTableCore) BefRtBeanUtil.getDboRtService().getDatabaseObject(bizEntityObject.getRefObjectName());
        this.entityDac = entityDac;
        if (dbo == null) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2008, bizEntityObject.getBelongModel().getName(),
                    bizEntityObject.getName(), bizEntityObject.getRefObjectName());

        }
        isChildAdaptor = bizEntityObject.getParentObject() != null;
        tableAlias = getWrappedAlias(bizEntityObject.getCode());
        if (isChildAdaptor) {
            String parentIdEleId = bizEntityObject.getKeys().get(0).getSourceElement();
            parentIdEle = bizEntityObject.findElement(parentIdEleId);
        }
        initParallelTableDboInfo();
        initAssociations();
        initParallelTableInfos();
        initColumns();
        sqlGenerator = getSqlGenerator(gspDbType);
    }

    //兼容处理IDP年度表
    private String getWrappedAlias(String alias) {
        alias = alias.trim();
        alias = alias.replace("$", "");
        return alias;
    }

    public CefEntityResInfoImpl getEntityResInfo() {
        //变量缓存?
        return (CefEntityResInfoImpl) EngineBEManager.getCefModelResourceInfo(bizEntityObject.getBelongModel()).getCustomResource(bizEntityObject.getCode());
    }

    @Override
    public LogicDeleteInfo getLogicDeleteInfo() {
        LogicDeleteInfo logicDeleteInfo = new LogicDeleteInfo(false, "IsDelete");
        if (this.bizEntityObject.getLogicDeleteControlInfo() != null && this.bizEntityObject.getLogicDeleteControlInfo().getEnableLogicDelete()) {
            logicDeleteInfo.setEnableLogicDelete(true);
            for (IGspCommonField gspCommonField : bizEntityObject.getContainElements()) {
                if (gspCommonField.getID().equals(bizEntityObject.getLogicDeleteControlInfo().getLogicDeleteControlElementId())) {
                    logicDeleteInfo.setLabelId(gspCommonField.getLabelID());
                    break;
                }
            }
        }
        return logicDeleteInfo;
    }

    @Override
    protected String getDboID() {
        return bizEntityObject.getRefObjectName();
    }

    //region PropertyColumn
    @Override
    protected boolean hasPropColumnMappping() {
        return propIndexMappingDict != null;
    }

    @Override
    protected HashMap<String, Integer> getPropertyColumnMapping() {
        return propIndexMappingDict;
    }

    @Override
    protected void setPropertyColumnMapping(HashMap<String, Integer> mappingDict) {
        propIndexMappingDict = mappingDict;
    }
    //endregion

    /**
     * 子表获取关联的父对象字段编号
     * @return
     */
    public String getParentObjectColumnCode(){
        IGspCommonElement parentElement = null;
        String parentElementId = bizEntityObject.getKeys().get(0).getTargetElement();
        parentElement = bizEntityObject.getParentObject().findElement(parentElementId);

        if (parentElement == null) {
            parentElement = bizEntityObject.getParentObject().getIDElement();
        }
        if(parentElement != null){
            return parentElement.getLabelID();
        }
        return "";
    }
    /**
     * 初始化字段集合信息
     */
    @Override
    protected void initColumns() {
        for (IGspCommonField element : bizEntityObject.getContainElements()) {
            if (element.getObjectType() == GspElementObjectType.DynamicProp) {
                continue;
            }
            if (element.getIsVirtual()) {
                initColumn((IGspCommonElement) element, (IGspCommonElement) element, null);
                continue;
            }
            if (element.getIsUdt()) {
                initUdtColumn((IGspCommonElement) element);
            } else {
                initNormalColumn((IGspCommonElement) element);
            }
        }
    }

    /**
     * 初始化平行表信息
     */
    protected void initParallelTableInfos() {
        if (this.bizEntityObject.getParallelTableCollection() == null || this.bizEntityObject.getParallelTableCollection().isEmpty()) {
            return;
        }
        for (ParallelTable parallelTable : this.bizEntityObject.getParallelTableCollection()) {
            ParallelTableInfo parallelTableInfo = new ParallelTableInfo();
            parallelTableInfo.setParallelTableCode(parallelTable.getCode());
            parallelTableInfo.setDboId(parallelTable.getRefDboID());
            String tableCode = this.parallelTableDboInfos.get(parallelTable.getID()).getCode();
            parallelTableInfo.setTableCode(tableCode);
            DatabaseObjectTableCore dbo = parallelTableDboInfos.get(parallelTable.getID());
            DatabaseObjectColumn idColumn = dbo.getColumnById(parallelTable.getPrimaryKey());
            parallelTableInfo.setPrimaryKeyCode(idColumn.getCode());

            if(org.apache.commons.lang3.StringUtils.isNotEmpty(parallelTable.getParentID())){
                DatabaseObjectColumn parentIdColumn = dbo.getColumnById(parallelTable.getParentID());
                if(parentIdColumn == null){
                    throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2038, parallelTable.getCode(), parallelTable.getParentID());
                }
                parallelTableInfo.setParentIdCode(parentIdColumn.getCode());
            }

            this.parallelTableInfos.put(parallelTable.getCode(), parallelTableInfo);
        }
    }

    private DatabaseObjectTableCore getParallelTableRefDbo(String parallelTableId) {
        return this.parallelTableDboInfos.get(parallelTableId);
    }

    private DatabaseObjectTableCore getParallelTableDbo(String parallelTableId) {
        return (DatabaseObjectTableCore) BefRtBeanUtil.getDboRtService().getDatabaseObject(parallelTableId);
    }

    private void initUdtColumn(IGspCommonElement element) {
        //单值UDT、关联UDT、多值单列UDT对应的childElements大小是1
        //dodo 多值多列也有可能只设置了一列
        boolean isMultiColumn = false;
        UnifiedDataTypeDef unifiedDataTypeDef = (UnifiedDataTypeDef) CMEngineUtil.getMetadata(element.getUdtID()).getContent();
        if (unifiedDataTypeDef instanceof ComplexDataTypeDef) {
            if (((ComplexDataTypeDef) unifiedDataTypeDef).getDbInfo().getMappingType() == ColumnMapType.MultiColumns) {
                isMultiColumn = true;
            }
        }
        GspBizEntityElement gspBizEntityElement = (GspBizEntityElement) element;
        DatabaseObjectTableCore tempDbo = null;
        if (!org.apache.commons.lang3.StringUtils.isEmpty(gspBizEntityElement.getRefParallelTableId())) {
            tempDbo = getParallelTableRefDbo(gspBizEntityElement.getRefParallelTableId());
        } else {
            tempDbo = dbo;
        }
        if (!isMultiColumn) {
            var column = tempDbo.getColumnById(element.getColumnID());
            if (column == null) {
                throwDboColumnNotFoundException(element, element.getBelongObject());
            }
            if (tempDbo != dbo) {
                String parallelTableCode = this.parallelTableMap.get(gspBizEntityElement.getRefParallelTableId()).getCode();
                initColumn(element, element, column, parallelTableCode);
            } else {
                initColumn(element, element, column);
            }
            return;
        }

        //多值多列UDT
        for (IGspCommonField childElement : element.getChildElements()) {
            if (!(childElement instanceof IGspCommonElement)) {
                continue;
            }
            IGspCommonElement childEle = (IGspCommonElement) childElement;
            var column = tempDbo.getColumnById(childEle.getColumnID());
            if (column == null) {
                throwDboColumnNotFoundException(childEle, element.getBelongObject());
            }
            if (unifiedDataTypeDef == null)
                unifiedDataTypeDef = (UnifiedDataTypeDef) CMEngineUtil.getMetadata(element.getUdtID()).getContent();
            ComplexDataTypeDef complexDataTypeDef = (ComplexDataTypeDef) unifiedDataTypeDef;
            String refedUdtElementID = element.getMappingRelation().getMappingInfo(childEle.getID());
            IGspCommonField refedElement = complexDataTypeDef.findElement(refedUdtElementID);
            if (refedElement == null) {
                throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2037, element.getLabelID(), childEle.getID(), complexDataTypeDef.getID(), bizEntityObject.getBelongModel().getId(), bizEntityObject.getId(), element.getID());
            }
            if (childEle.getObjectType() != refedElement.getObjectType()) {
                childEle.setObjectType(refedElement.getObjectType());
                childEle.setEnumIndexType(refedElement.getEnumIndexType());
                childEle.setContainEnumValues(refedElement.getContainEnumValues());
            }
            childEle.setIsUdt(true);
            if (tempDbo != dbo) {
                String parallelTableCode = this.parallelTableMap.get(gspBizEntityElement.getRefParallelTableId()).getCode();
                initColumn(childEle, element, column, parallelTableCode);
            } else {
                initColumn(childEle, element, column);
            }

        }

    }

    /**
     * 初始化列信息
     *
     * @param element
     */
    private void initNormalColumn(IGspCommonElement element) {
        GspBizEntityElement gspBizEntityElement = (GspBizEntityElement) element;
        DatabaseObjectColumn column = null;
        if (!org.apache.commons.lang3.StringUtils.isEmpty(gspBizEntityElement.getRefParallelTableId())) {
            DatabaseObjectTableCore parallelTableDbo = getParallelTableRefDbo(gspBizEntityElement.getRefParallelTableId());
            column = parallelTableDbo.getColumnById(element.getColumnID());
            String tableCode = this.parallelTableMap.get(gspBizEntityElement.getRefParallelTableId()).getCode();
            initColumn(element, element, column, tableCode);
        } else {
            column = dbo.getColumnById(element.getColumnID());
            initColumn(element, element, column);
        }
    }

    /**
     * 抛出dbo列找不到异常
     */
    private void throwDboColumnNotFoundException(IGspCommonElement element, IGspCommonObject commonObject) {
        throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2003, false, element.getLabelID(),
                ExceptionUtil.getBEInfo(commonObject), ExceptionUtil.getBizEntityObjInfo(commonObject),
                ExceptionUtil.getBizElementInfo(element), dbo.getId());
    }

    private void initColumn(IGspCommonElement element, IGspCommonElement belongElement, DatabaseObjectColumn dboColumn) {
        initColumn(element, belongElement, dboColumn, null);
    }

    private void initColumn(IGspCommonElement element, IGspCommonElement belongElement, DatabaseObjectColumn dboColumn, String parallelTableCode) {
        GspDbDataType dataType = GspDbDataType.VarChar;
        DbColumnInfo dbColumnInfo = new DbColumnInfo();
        dbColumnInfo.setColumnName(element.getLabelID());
        boolean isAssociateRefElement = false;
        //init方法的都是基础字段
        if (element.getIsVirtual()) {
            dbColumnInfo.setDbColumnName("");
            dbColumnInfo.setVirtual(true);
            dataType = getVirtualDbType(element);
        } else {
            dataType = DataTypeConvertor.transDataType(dboColumn.getDataType());
            dbColumnInfo.setDbColumnName(isAssociateRefElement ? "" : dboColumn.getCode());
        }

        dbColumnInfo.setColumnType(dataType);
        dbColumnInfo.setLength(element.getLength());
        dbColumnInfo.setPrecision(element.getPrecision());
        dbColumnInfo.setDefaultValue(null);
        dbColumnInfo.setIsPrimaryKey(isPrimaryKey(element));
        dbColumnInfo.setIsAssociateRefElement(isAssociateRefElement);
        dbColumnInfo.setIsMultiLang(element.getIsMultiLanguage());
        dbColumnInfo.setIsParentId(isParentColumn(element));
        dbColumnInfo.setIsUdtElement(element.getIsUdt());
        dbColumnInfo.setIsAssociation(element.getObjectType() == GspElementObjectType.Association);
        dbColumnInfo.setIsEnum(element.getObjectType() == GspElementObjectType.Enum);
        dbColumnInfo.setBelongElementLabel(belongElement == null ? "" : belongElement.getLabelID());
        dbColumnInfo.setTypeTransProcesser(getTypeProcesser(dataType, element, dboColumn));
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(parallelTableCode)) {
            dbColumnInfo.setRefParallelTableCode(parallelTableCode);
        }
        if (element instanceof GspCommonField) {
            GspCommonField gspCommonField = (GspCommonField) element;
            if (gspCommonField.getSelectFieldRepoConfig() != null && !com.inspur.edp.cef.spi.jsonser.base.StringUtils.isNullOrEmpty(gspCommonField.getSelectFieldRepoConfig().getConfigClassImpl())) {
                dbColumnInfo.setFieldReposExtendConfigId(gspCommonField.getSelectFieldRepoConfig().getConfigId());
            }
        }

        super.getContainColumns().add(dbColumnInfo);
    }

    private GspDbDataType getVirtualDbType(IGspCommonElement element) {
        GspDbDataType dbDataType = GspDbDataType.VarChar;
        switch (element.getMDataType()) {
            case String:
                dbDataType = GspDbDataType.VarChar;
                break;
            case Integer:
                dbDataType = GspDbDataType.Int;
                break;
            case Decimal:
                dbDataType = GspDbDataType.Decimal;
                break;
            case Boolean:
                dbDataType = GspDbDataType.Char;
                break;
            case Date:
                dbDataType = GspDbDataType.Date;
                break;
            case DateTime:
                dbDataType = GspDbDataType.DateTime;
                break;
            case Text:
                dbDataType = GspDbDataType.Clob;
                break;
            case Binary:
                dbDataType = GspDbDataType.Blob;
                break;
            case Geometry:
                dbDataType = GspDbDataType.Geometry;
                break;
        }
        return dbDataType;
    }

    private ITypeTransProcesser getTypeProcesser(
            GspDbDataType dbType, IGspCommonElement element, DatabaseObjectColumn dboColumn) {
        int length = (dboColumn == null) ? 0 : dboColumn.getLength();
        if (element.getObjectType() == GspElementObjectType.Enum) {
            switch (dbType) {
                case Int:
                    return new EnumEngineIntProcesser((GspBizEntityElement) element);
                case VarChar:
                case NVarChar:
                    return new EnumEngineVarcharProcesser((GspBizEntityElement) element);
            }
        }
        switch (element.getMDataType()) {
            case Boolean:
                if (dbType == GspDbDataType.Int) {
                    return Bool2IntProcesser.getInstacne();
                }
                if (dbType == GspDbDataType.Char || dbType == GspDbDataType.VarChar) {
                    return Bool2CharProcesser.getInstacne();
                }
                if (dbType == GspDbDataType.Boolean)
                    return BoolTransProcesser.getInstacne();
                break;
            case Date:
                if ((dbType == GspDbDataType.Char || dbType == GspDbDataType.VarChar) && length == 8)
                    return DateTime2Char8Processer.getInstacne();
                return DateTimeTransProcesser.getInstacne();
            case DateTime:
                return DateTimeTransProcesser.getInstacne();
            case Decimal:
                if (dbType == GspDbDataType.Decimal) {
                    return DecimalTransProcesser.getInstacne();
                }
                break;
            case Integer: {
                if (dbType == GspDbDataType.SmallInt) {
                    return SmallIntTransProcesser.getInstance();
                }
                if (dbType == GspDbDataType.Int) {
                    return IntTransProcesser.getInstacne();
                }
                //有自己改dbo的，be上是整数，dbo是long，但是又没存过超过int最大值的数，所以这里得兼容
                if (dbType == GspDbDataType.Long) {
                    return IntTransProcesser.getInstacne();
                }
                break;
            }
            case String:
                if (dbType == GspDbDataType.Int) {
                    return VarcharIntTransProcesser.getInstacne();
                } else if (dbType == GspDbDataType.Long) {
                    return VarcharLongTransProcesser.getInstance();
                }
                return VarcharTransProcesser.getInstacne();
            case Text:
                return ClobTransProcesser.getInstacne();
            case Binary:
                return BinaryTransProcesser.getInstacne();
            case Geometry:
                return GeometryTransProcesser.getInstacne();
        }

        throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2010, false,
                element.getLabelID(),
                ExceptionUtil.getBEInfo(bizEntityObject),
                ExceptionUtil.getBizEntityObjInfo(bizEntityObject),
                ExceptionUtil.getBizElementInfo(element),
                element.getMDataType().toString(),
                dbType.toString(),
                dboColumn == null ? "" : dboColumn.getDataType().toString());
    }

    private boolean isPrimaryKey(IGspCommonElement element) {
        return bizEntityObject.getIDElement().getID().equals(element.getID());
    }

    private boolean isParentColumn(IGspCommonElement element) {
        if (isChildAdaptor) {
            return parentIdEle.getID().equals(element.getID());
        }
        return false;
    }
    // endregion

    //region Filter & Sort
    @Override
    protected ArrayList<FilterCondition> getDefaultFilterCondition() {
        return null;
    }

    @Override
    protected ArrayList<SortCondition> getDefaultSortCondition() {
        return null;
    }

    //endregion

    @Override
    public String getPrimaryKey() {
        return dbo.getColumnById(bizEntityObject.getIDElement().getColumnID()).getCode();
    }

    @Override
    public String getTableAlias() {
        return tableAlias;
    }

    @Override
    public void setTableAlias(String value) {
        tableAlias = value;
    }

    @Override
    protected String getConfigId() {
        return bizEntityObject.getBelongModel().getGeneratedConfigID();
    }

    //region Delete
    @Override
    protected String innerGetDeleteSql() {
        return getDeleteSqlStr("");
    }

    protected String innerGetDeleteSql(String parallelTableCode) {
        return getDeleteSqlStr(parallelTableCode);
    }


    @Override
    protected String getDeleteSqlBatch() {
        return getDeleteSqlStr("");
    }

    private String getDeleteSqlStr(String parallelTableCode) {
        if(org.apache.commons.lang3.StringUtils.isEmpty(parallelTableCode)){
            deleteSql = String.format(sqlGenerator.getDeleteSql(), "@TableName@", tableAlias);
        }
        else{
            ParallelTableInfo parallelTable = this.parallelTableInfos.get(parallelTableCode);
            deleteSql = String.format(sqlGenerator.getDeleteSql(), "@TableName@", parallelTable.getParallelTableCode());
        }
        return deleteSql;
    }

    //endregion

    // region Insert
    @Override
    protected String innerGetInsertSql() {
        return innerGetInsertSql("");
    }

    protected String innerGetInsertSql(String parallelTableCode) {
        batchInsert = true;
        initInsertSql(parallelTableCode);
        //todo 去掉类成员变量
        return insertSql;
    }

    private void initInsertSql(String parallelTableCode) {
        String sql = sqlGenerator.getInsertSql();
        buildInsertColumnsAndParams(parallelTableCode);
        insertSql = String.format(sql, "@TableName@", insertColumns, insertParams);
    }

    private String insertColumns;
    private String insertParams;

    private void buildInsertColumnsAndParams(String parallelTableCode) {
        //跟之前一致，并且在当前实例上做缓存。
        StringBuilder columnNames = new StringBuilder();
        StringBuilder valueParameter = new StringBuilder();
        ParallelTableInfo parallelTableInfo = this.parallelTableInfos.get(parallelTableCode);
        if(parallelTableInfo != null){
            columnNames.append(parallelTableInfo.getPrimaryKeyCode());
            valueParameter.append("?");
        }

        for (DbColumnInfo columnInfo : getContainColumns()) {
            if (columnInfo.getIsAssociateRefElement()) {
                continue;
            }
            if (columnInfo.isVirtual()) {
                continue;
            }
            if (!columnInfo.getIsPersistent()) {
                continue;
            }
            if(!parallelTableCode.equalsIgnoreCase(columnInfo.getRefParallelTableCode())){
                continue;
            }
            if (!columnNames.toString().isEmpty()) {
                columnNames.append(", ");
                valueParameter.append(", ");
            }
            if (columnInfo.getIsMultiLang()) {
                columnNames.append(KeyWordsManager.getColumnAlias(getMultiLangColumnName(columnInfo.getDbColumnName())));
            } else {
                columnNames.append(KeyWordsManager.getColumnAlias(columnInfo.getDbColumnName()));
            }
            if (columnInfo.getColumnType() == GspDbDataType.Geometry) {
                valueParameter.append(this.getGeometryParameterizedSql());
            } else {
                valueParameter.append("?");
            }
        }

        if (TenantUtil.IsDiscriminator()) {
            String tenantColName = TenantUtil.GetTenantColumnName();
            columnNames.append(", ").append(tenantColName);
            valueParameter.append(", ").append("?");
        }

        insertColumns = columnNames.toString();
        insertParams = valueParameter.toString();
    }

    protected String getMultiLangColumnName(String columnName) {
        //string currentLanguage = I18NUtils.getCurrentLanguage();
        //return columnName + I18NUtils.getFieldSuffix(currentLanguage);
        return columnName + "@Language@";
    }

    //endregion

    //region Modify
    @Override
    protected String innerGetModifySql() {
        if (modifySql == null) {
            modifySql = String.format("Update %1$s Set", "@TableName@");
        }
        return modifySql;
    }

    //endregion

    @Override
    protected Object getPropertyChangeValue(String propertyName, Object propertyValue) {
        for (var element : bizEntityObject.getContainElements()) {
            IsEnableEncryption = ((IGspCommonElement) element).getIsEnableEncryption();
            if (element.getIsUdt()) {
                if (!element.getLabelID().equals(propertyName) && !containChildElementByLabelId(element, propertyName))
                    continue;
                return ReadUtils.getUdtValue(bizEntityObject, propertyName, element.getLabelID(), propertyValue, IsEnableEncryption);
            }
            if (!element.getLabelID().equals(propertyName))
                continue;
            if (element.getObjectType() == GspElementObjectType.Association)
                return ((AssociationInfo) propertyValue).getValue(element.getLabelID());
            if (element.getMDataType() == GspElementDataType.Date) {
                return this.getContainColumns().getItem(element.getLabelID()).
                        getTypeTransProcesser().transType(propertyValue);
            }
            propertyValue = encryptIfEnabled(propertyValue, IsEnableEncryption);
            return propertyValue;
        }
        throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2011, propertyName, getBEInfo(), getBizEntityObjInfo(bizEntityObject));
    }

    /**
     * 获取异常提示中的BE信息
     *
     * @return
     */
    private String getBEInfo() {
        return ExceptionUtil.getBEInfo(bizEntityObject);
    }

    /**
     * 获取异常提示中的对象信息
     *
     * @param commonObject
     * @return
     */
    private String getBizEntityObjInfo(IGspCommonObject commonObject) {
        return ExceptionUtil.getBizEntityObjInfo(commonObject);
    }

    /**
     * 获取异常提示中的字段信息
     *
     * @param element
     * @return
     */
    private String getBizElementInfo(IGspCommonField element) {
        return ExceptionUtil.getBizElementInfo(element);
    }


    //region GetData
    @Override
    protected String getGetDataByIdsSql() {
        if (retrieveBatchSql == null) {
            retrieveBatchSql = String.format(sqlGenerator.getRetrieveBatchSql(), "%1$s", "%2$s", tableAlias + "." + getContainColumns().getPrimaryKey().getDbColumnName(), "%3$s");
        }

        return retrieveBatchSql;
    }

    @Override
    protected String getGetDataByIdSql() {

        if (retrieveSql == null) {
            retrieveSql = String.format(
                    sqlGenerator.getRetrieveSql(),
                    "%1$s",
                    "%2$s",
                    tableAlias + "." + getContainColumns().getPrimaryKey().getDbColumnName(),
                    "%3$s");
        }
        return retrieveSql;
    }

    private String versionPropName;

    @Override
    protected String getVersionControlPropName() {
        if (!bizEntityObject.getIsRootNode()) {
            return "";
        }
        if (versionPropName == null) {
            String versionElementId = bizEntityObject.getBelongModel().getVersionContronInfo().getVersionControlElementId();
            if (StringUtils.isEmpty(versionElementId)) {
                versionPropName = "";
            } else {
                IGspCommonElement versionElement = bizEntityObject.findElement(versionElementId);
                if (versionElement == null) {
                    throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2012, getBEInfo(), getBizEntityObjInfo(bizEntityObject), versionElementId);
                }
                versionPropName = versionElement.getLabelID();
            }
        }
        return versionPropName;
    }
    //endregion

    //region JoinTable
    @Override
    public String getParentJoin() {
        return getParentJoin(null);
    }


    public String getParentJoin(ParallelTableInfo parallelTableInfo) {
        if (!isChildAdaptor)
            return null;
        return getJoinTable(parallelTableInfo);
    }

    private String getJoinTable(ParallelTableInfo parallelTableInfo) {
        String parentPrimaryKey = getParentTablePrimaryKey(); //{5}
        if (parentPrimaryKey.equals("") || parentPrimaryKey == null) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2013, getBEInfo(), getBizEntityObjInfo(bizEntityObject.getParentObject()));
        }
        String parentIdColumnName = "";
        if(parallelTableInfo == null){
            parentIdColumnName = getWrappedAlias(getTableAlias()) + "." + getParentIdColumnName();
        }
        else {
            parentIdColumnName =getWrappedAlias(parallelTableInfo.getParallelTableCode()) + "." + parallelTableInfo.getParentIdCode();
        }

        String parentAlias = bizEntityObject.getParentObject().getCode();
        parentAlias = parentAlias.trim();
        parentAlias = parentAlias.replace("$", "");
        //JOIN @ParentTableName@ @ParentTableAlias@ ON @ParentID@ = @PrimaryID@
        String joinSql = sqlGenerator.getInnerJoinTableName().replace("@ParentTableAlias@", parentAlias)
                .replace("@ParentID@", parentIdColumnName)
                .replace("@PrimaryID@", parentPrimaryKey);
        return joinSql;
    }

    /**
     * 获取主表的关联字段
     * 默认子表记录主表的主键，IDP支持记录非主键字段，这里要支持一下
     *
     * @return
     */
    private String getParentTablePrimaryKey() {
        IGspCommonElement parentElement = null;
        String parentElementId = bizEntityObject.getKeys().get(0).getTargetElement();
        parentElement = bizEntityObject.getParentObject().findElement(parentElementId);

        String alias = bizEntityObject.getParentObject().getCode();
        alias = alias.trim();
        alias = alias.replace("$", "");
        if (parentElement == null) {
            parentElement = bizEntityObject.getParentObject().getIDElement();
            if (parentElement == null)
                throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2014, false, alias, getBEInfo(), getBizEntityObjInfo(bizEntityObject.getParentObject()));
        }


        var parentDbo = (DatabaseObjectTableCore) BefRtBeanUtil.getDboRtService()
                .getDatabaseObject(bizEntityObject.getParentObject().getRefObjectName());


        var dbColumn = parentDbo.getColumnById(parentElement.getColumnID());
        if (dbColumn == null)
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2015, false, parentDbo.getCode(), getBEInfo(),
                    getBizEntityObjInfo(bizEntityObject.getParentObject()), bizEntityObject.getParentObject().getRefObjectName(),
                    ExceptionUtil.getBizElementInfo(parentElement));
        return alias + "." + dbColumn.getCode();
    }

    /**
     * 子表ParentID是否关联主表的主键字段
     *
     * @return
     */
    public boolean isAssoParentPrimaryKey() {
        IGspCommonElement parentElement = null;
        if (bizEntityObject.getParentObject() == null)
            return false;
        String parentElementId = bizEntityObject.getKeys().get(0).getTargetElement();
        parentElement = bizEntityObject.getParentObject().findElement(parentElementId);

        IGspCommonElement idElement = bizEntityObject.getParentObject().getIDElement();
        if (parentElement != null && idElement != null && idElement.getID().equalsIgnoreCase(parentElement.getID())) {
            return true;
        }
        return false;
    }

    private String getParentIdColumnName() {
        if (parentIdEle == null)
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2016, false, getBEInfo(), getBizEntityObjInfo(bizEntityObject));
        var parentIdColumn = dbo.getColumnById(parentIdEle.getColumnID());
        if (parentIdColumn == null)
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2015, dbo.getCode(), getBEInfo(), getBizEntityObjInfo(bizEntityObject), dbo.getId(),
                    ExceptionUtil.getBizElementInfo(parentIdEle));
        return parentIdColumn.getCode();
    }

    @Override
    protected String getJoinTableName() {
        return sqlGenerator.getJoinTableName();
    }

    //endregion

    @Override
    public ICefData createInstance(ICefReader iCefReader) {
        return createInstance(null, iCefReader);
    }

    /**
     * 根据数据库查询值创建Data对象
     *
     * @param reader
     * @return
     */
//    @Override
    public ICefData createInstance(List<String> filterFields, ICefReader reader) {
        IEntityData data = null;
        if (isChildAdaptor)
            data = new EngineChildData(bizEntityObject, getResInfo(bizEntityObject.getCode()));
        else
            data = new EngineRootData(bizEntityObject, getResInfo(bizEntityObject.getCode()));

        if (filterFields != null && filterFields.size() > 0) {
            for (String field : filterFields) {
                IGspCommonField element = bizEntityObject.getContainElements().stream().filter(item -> item.getLabelID().equalsIgnoreCase(field)).findFirst().orElse(null);
                //有可能传的过滤字段不正确,取出来的是null
                if (element != null) {
                    processElement(reader, data, element);
                }
            }
        } else {
            for (IGspCommonField element : bizEntityObject.getContainElements()) {
                processElement(reader, data, element);
            }
        }

        return data;
    }

    private void processElement(ICefReader reader, IEntityData data, IGspCommonField element) {
        if (element == null) {
            return;
        }
        if (element.getIsUdt()) {
            ReadUtils.setUdtData(reader, false, data, (IGspCommonElement) element);
        } else if (element.getObjectType() != GspElementObjectType.DynamicProp) {
            Object value = ReadUtils.getValue((IGspCommonElement) element, reader);
            data.setValue(element.getLabelID(), value);
        }
    }


    @Override
    public final EntityDac getEntityDac() {
        return entityDac;
    }

    private BeModelResInfo modelResInfo;

    private CefEntityResInfoImpl getResInfo(String nodeCode) {
        if (modelResInfo == null) {
            modelResInfo = (BeModelResInfo) EngineBEManager.getCefModelResourceInfo(bizEntityObject.getBelongModel());
        }
        return (CefEntityResInfoImpl) modelResInfo.getCustomResource(nodeCode);
    }

    @Override
    public Object getPersistenceValue(String colName, ICefData data) {
        //todo 按照dbColumnInfo进行循环可以提高性能
        for (var element : bizEntityObject.getContainElements()) {
            //udt字段
            if (element.getIsUdt()) {
                if (!element.getLabelID().equals(colName) && !containChildElementByLabelId(element, colName))
                    continue;
                var udtData = data.getValue(element.getLabelID());
                return ReadUtils.getUdtValue(bizEntityObject, colName, element.getLabelID(), udtData);
            }

            //非udt字段
            if (!element.getLabelID().equals(colName))
                continue;

            Object obj = null;
            try {
                obj = this.getContainColumns().getItem(element.getLabelID()).
                        getTypeTransProcesser().transType(data.getValue(element.getLabelID()));
            } catch (Exception ex) {
                throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2036, ex,
                        colName,
                        ExceptionUtil.getBEInfo(bizEntityObject),
                        ExceptionUtil.getBizEntityObjInfo(bizEntityObject),
                        ExceptionUtil.getBizElementInfo(element));
            }
            return obj;
        }

        throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2011, colName, ExceptionUtil.getBEInfo(bizEntityObject), ExceptionUtil.getBizEntityObjInfo(bizEntityObject));
    }

    private static boolean containChildElementByLabelId(IGspCommonField element, String lableId) {
        for (IGspCommonField childElement :
                element.getChildElements()) {
            if (childElement.getLabelID().equals(lableId))
                return true;
        }
        return false;
    }

//    @Override
//    public HashMap<String, String> getAssosPropDBMapping(String propName)//生成引用解析的时候 会用到。
//    {
//        for (IGspCommonField element : this.bizEntityObject.getContainElements()) {
//            if (!element.getLabelID().equalsIgnoreCase(propName))
//                continue;
//            //如果是多值多列UDT
//            if (element.getIsUdt()) {
//                UnifiedDataTypeDef udt = CMEngineUtil.getMetadataContent(element.getUdtID());
//                if (udt == null)
//                    throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2017, false, element.getLabelID(),element.getUdtID(),
//                            getBEInfo(), getBizEntityObjInfo(bizEntityObject), getBizElementInfo(element));
//
//
//
//
//                if (udt instanceof ComplexDataTypeDef) {
//                    ComplexDataTypeDef complexUdt = (ComplexDataTypeDef) udt;
//                    return getMultiUdtAssosPropDBMapping(complexUdt, element);
//                }
//            }
//            //todo 后续关联的也加上
//            break;
//        }
//        return new HashMap<String, String>();
//    }

    public HashMap<String, String> getMultiUdtAssosPropDBMapping(ComplexDataTypeDef complexUdt, IGspCommonField item) {
        HashMap<String, String> map = new HashMap<String, String>();
        if (complexUdt.getDbInfo().getMappingType() == ColumnMapType.SingleColumn)
            return map;
        for (IGspCommonField field : complexUdt.getContainElements()) {
            map.put(field.getLabelID(), item.getLabelID() + "_" + field.getLabelID());
        }
        return map;
    }

    @Override
    public Object readProperty(String propertyName, ICefReader reader) {
        for (var element : bizEntityObject.getAllElementList(true)) {
            if (!element.getLabelID().equals(propertyName))
                continue;
            if (element.getIsUdt()) {
                return ReadUtils.getUdtData(false, reader, (IGspCommonElement) element);
            }
            return ReadUtils.getValue((IGspCommonElement) element, reader);
        }
        return null;
    }

    //endregion

    //region association
    @Override
    protected void initAssociations() {
        for (IGspCommonField field : bizEntityObject.getContainElements()) {
            if (field.getHasAssociation())
                initEleAssociations(field);
        }
    }

    /**
     * 初始化平行表信息
     */
    private void initParallelTableDboInfo() {
        if (this.bizEntityObject.getParallelTableCollection() == null || this.bizEntityObject.getParallelTableCollection().isEmpty()) {
            return;
        }
        for (ParallelTable parallelTable : this.bizEntityObject.getParallelTableCollection()) {
            DatabaseObjectTableCore parallelTableDbo = getParallelTableDbo(parallelTable.getRefDboID());
            this.parallelTableDboInfos.put(parallelTable.getID(), parallelTableDbo);

            this.parallelTableMap.put(parallelTable.getID(), parallelTable);
        }
    }

    private void initEleAssociations(IGspCommonField element) {
        if (element.getChildAssociations() == null || element.getChildAssociations().isEmpty()) {
            return;
        }
        for (GspAssociation association : element.getChildAssociations()) {
            initEleAssociation(association);
        }

    }

    private void initEleAssociation(GspAssociation association) {
        GspBusinessEntity refModel = CMEngineUtil.getMetadataContent(association.getRefModelID());
        if (refModel == null) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2018, false, association.getBelongElement().getName(), association.getRefModelName(),
                    ExceptionUtil.getBEInfo(bizEntityObject), ExceptionUtil.getBizEntityObjInfo(bizEntityObject),
                    ExceptionUtil.getBizElementInfo(association.getBelongElement()), ExceptionUtil.getAssociationInfo(association));
        }

        IGspCommonField belongElement = association.getBelongElement();
        String sourceId = getSourceElementId(association);
        if (sourceId == null || "".equals(sourceId)) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2019, false, belongElement.getName(), ExceptionUtil.getBEInfo(bizEntityObject), ExceptionUtil.getBizEntityObjInfo(bizEntityObject),
                    ExceptionUtil.getBizElementInfo(belongElement), association.getId(), association.getKeyCollection().get(0).getTargetElement());
        }

        IRootRepository refRepository = BefRtBeanUtil.getBefRepositoryFactory().createRepository(refModel.getGeneratedConfigID());
        HashMap<String, String> refColumns = new HashMap<>();


        for (IGspCommonField refElement : association.getRefElementCollection()) {
            IGspCommonElement targetElement = refModel.findElementById(refElement.getRefElementId());
            if (targetElement == null) {
                throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2020, refElement.getLabelID(), refModel.getCode(), ExceptionUtil.getBEInfo((IGspCommonObject) association.getBelongElement().getBelongObject()),
                        ExceptionUtil.getBizEntityObjInfo((IGspCommonObject) belongElement.getBelongObject()),
                        ExceptionUtil.getBizElementInfo(belongElement),
                        ExceptionUtil.getBizElementInfo(refElement),
                        ExceptionUtil.getBEInfo(refModel));
            }
            refColumns.put(refElement.getLabelID(), targetElement.getLabelID());
        }

        IGspCommonObject targetObject = null;
        //这个If永远走不到啊。。。
        if (StringUtils.isEmpty(association)) {
            targetObject = refModel.getMainObject();
        } else {
            targetObject = getObjectByID(refModel, association.getRefObjectID());
        }
        if (targetObject == null) {
            throw new BefRespositoryException(ErrorCodes.BEF_ENGINE_2021,
                    belongElement.getLabelID(),
                    refModel.getCode(), ExceptionUtil.getBEInfo((GspBizEntityObject) association.getBelongElement().getBelongObject()),
                    ExceptionUtil.getBizEntityObjInfo((IGspCommonObject) belongElement.getBelongObject()),
                    ExceptionUtil.getBizElementInfo(belongElement),
                    ExceptionUtil.getAssociationInfo(association));
        }
        IGspCommonElement sourceElement = targetObject.findElement(sourceId);

        ArrayList<com.inspur.edp.cef.repository.assembler.AssoCondition> conditionList = new ArrayList<com.inspur.edp.cef.repository.assembler.AssoCondition>();
        if (association.getAssoConditions() != null && association.getAssoConditions().size() > 0) {
            for (AssoCondition condition : association.getAssoConditions()) {
                com.inspur.edp.cef.repository.assembler.AssoCondition condi = new com.inspur.edp.cef.repository.assembler.AssoCondition();
                condi.setLeftNodeCode(condition.getLeftNodeCode());
                condi.setLeftField(condition.getLeftField());
                condi.setOperator(condition.getOperator());
                condi.setRightNodeCode(condition.getRightNodeCode());
                condi.setRightField(condition.getRightField());
                condi.setValue(condition.getValue());
                conditionList.add(condi);
            }
        }

        ArrayList<com.inspur.edp.cef.entity.repository.AssoVariable> variables = new ArrayList<>();
        if (association.getAssoVariables() != null && association.getAssoVariables().size() > 0) {

            for (AssoVariable variable : association.getAssoVariables()) {
                com.inspur.edp.cef.entity.repository.AssoVariable assoVariable = new com.inspur.edp.cef.entity.repository.AssoVariable();
                assoVariable.setVarCode(variable.getVarCode());
                assoVariable.setVarValue(variable.getVarValue());
                variables.add(assoVariable);
            }
        }

        com.inspur.edp.cef.repository.assembler.AssociationInfo associationInfo = new com.inspur.edp.cef.repository.assembler.AssociationInfo();
        associationInfo.setNodeCode(targetObject.getCode());
        associationInfo.setSourceColumn(belongElement.getLabelID());
        associationInfo.setTargetColumn(sourceElement.getLabelID());
        associationInfo.setRefRepository(refRepository);
        associationInfo.setRefColumns(refColumns);
        associationInfo.setConfigId(refModel.getGeneratedConfigID());
        associationInfo.setWhere(association.getWhere());
        associationInfo.setAssoConditions(conditionList);
        associationInfo.setAssoVariables(variables);
        associationInfo.setRefTableAlias(association.getRefTableAlias());
        //Refuse的时候是启用删除检查
        if (association.getDeleteRuleType() == GspDeleteRuleType.Refuse) {
            associationInfo.setDeleteCheckState(DeleteCheckState.Enabled);
        } else {
            associationInfo.setDeleteCheckState(DeleteCheckState.Disabled);
        }

        super.getAssociationInfos().add(associationInfo);

    }

    private String getSourceElementId(GspAssociation association) {
        IGspCommonField belongElement = association.getBelongElement();
        for (GspAssociationKey associationKey : association.getKeyCollection()) {
            if (associationKey.getTargetElement().equals(belongElement.getID())) {
                return associationKey.getSourceElement();
            }
        }
        return null;
    }

    private IGspCommonObject getObjectByID(IGspCommonModel model, String objectID) {
        //TODO: getAllObjectList逻辑较复杂,再根据id查找性能稍差,大部分场景都是关联主表先临时优化
        if (model.getMainObject().getID().equals(objectID)) {
            return model.getMainObject();
        }
        for (IGspCommonObject item : model.getAllObjectList()) {
            if (objectID.equals(item.getID())) {
                return item;
            }
        }
        return null;
    }

    //endregion

    @Override
    protected String getNodeCode() {
        return bizEntityObject.getCode();
    }
}
